package evq.codec.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import evq.codec.core.converters.Converter;
import evq.codec.core.entities.Dictionary;
import evq.codec.core.entities.EncodeMethod;
import evq.codec.core.entities.Pgm;
import evq.codec.core.entities.PgmQuanta;
import evq.codec.core.entities.Profiler;
import evq.codec.core.entities.Quanta;
import evq.codec.core.entities.Vector;
import evq.codec.core.managers.FileManager;
import evq.codec.core.managers.LbgManager;
import evq.codec.core.managers.ManagersFactory;
import evq.codec.core.managers.VqManager;
import evq.codec.core.managers.VqMetricsManager;
import evq.codec.core.managers.VqManager.QuantizeType;

public class EvqApplication {

	private static EvqApplication instance = new EvqApplication();
	
	private VqManager vqManager;
	private LbgManager lbgManager;
	private FileManager fileManager;
	private VqMetricsManager vqMetricsManager;
	
	private EvqApplication() {
		ManagersFactory managersFactory = ManagersFactory.getInstance();
		
		this.vqManager = managersFactory.getVqManager();
		this.lbgManager = managersFactory.getLbgManager();
		this.fileManager = managersFactory.getFileManager();
		this.vqMetricsManager = managersFactory.getVqMetricsManager();
	}
	
	public static EvqApplication getInstance() {
		return instance;
	}

	public void createDictionary(Collection<String> trainningSetFilesPaths, 
			int dictionarySize, int quantizatorDimension, String dictionaryFilePath) {
		
		List<Vector> trainningSet = new ArrayList<Vector>();
		for (String pgmFilePath : trainningSetFilesPaths) {
			Pgm pgm = this.fileManager.loadPgmFile(pgmFilePath);
			List<Vector> vectors = Converter.convertPgmToVectors(pgm, quantizatorDimension);
			trainningSet.addAll(vectors);
		}
		
		Dictionary dictionary = this.lbgManager.createDictionary(trainningSet, dictionarySize);
		this.fileManager.createDictionaryFile(dictionary, dictionaryFilePath);
	}
	
	public Profiler encodePgm(String pgmFilePath, String dictionaryFilePath, 
			String encodedFilePath, EncodeMethod encodeMethod) {
		
		Profiler profiler = new Profiler();
		
		Pgm pgm = this.fileManager.loadPgmFile(pgmFilePath);
		Dictionary dictionary = this.fileManager.loadDictionaryFile(dictionaryFilePath);
		
		List<Vector> vectors = Converter.convertPgmToVectors(pgm, dictionary.getQuantizatorDimension());
		
		Quanta quanta = this.vqManager.quantize(vectors, dictionary, QuantizeType.PGM, encodeMethod, profiler);
		((PgmQuanta)quanta).setWidth(pgm.getWidth());
		((PgmQuanta)quanta).setHeight(pgm.getHeight());
		((PgmQuanta)quanta).setGrayScale(pgm.getGrayscale());
		
		this.fileManager.createQuantaFile(quanta, encodedFilePath);
		
		return profiler;
	}
	
	public void decodePgm(String encodedFilePath, String dictionaryFilePath, 
			String pgmFilePath) {
		
		PgmQuanta quanta = (PgmQuanta) this.fileManager.loadQuantaFile(encodedFilePath);
		Dictionary dictionary = this.fileManager.loadDictionaryFile(dictionaryFilePath);
		
		List<Vector> vectors = this.vqManager.unquantize(quanta, dictionary);
		
		Pgm pgm = Converter.convertVectorsToPgm(vectors, quanta.getWidth(), 
				quanta.getHeight(), quanta.getGrayScale());
		
		this.fileManager.createPgmFile(pgm, pgmFilePath);
	}
	
	public double calculateMse(String sourcePgmFilePath, String quantizedPgmFilePath) {
		Pgm sourcePgm = this.fileManager.loadPgmFile(sourcePgmFilePath);
		Pgm quantizedPgm = this.fileManager.loadPgmFile(quantizedPgmFilePath);
		
		return this.vqMetricsManager.calculateMse(sourcePgm, quantizedPgm);
	}
	
	public double calculatePsnr(String sourcePgmFilePath, String quantizedPgmFilePath) {
		Pgm sourcePgm = this.fileManager.loadPgmFile(sourcePgmFilePath);
		Pgm quantizedPgm = this.fileManager.loadPgmFile(quantizedPgmFilePath);
		
		return this.vqMetricsManager.calculatePsnr(sourcePgm, quantizedPgm);
	}
	
	public double codificationRate(String dictionaryFilePath) {
		Dictionary dictionary = this.fileManager.loadDictionaryFile(dictionaryFilePath);
		
		return this.vqMetricsManager.calculateCodificationRate(dictionary);
	}
	
}
